import './Workflow.scss';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { NodeModel, LineModel } from '../model/Model';
import { Paper } from 'react-raphael';
import { clearCreateNodeData } from '../../reducer/controller';
import * as _ from 'lodash';

import NodeAdapter from '../raphael/NodeAdapter';
import Line from '../raphael/Line';
import { getValidNodeRange, getRangeByPoint, getNodeSuggestRange } from '../../util/eventUtil';
import { Range, OperaType, LineType, ICreateNodeData, Point } from '../../reducer/const';

import { node, line } from './json.1';
// import { node, line } from './json';

interface IProps {
    /** 操作模式 */
    operaType: number;
    /** 执行新增节点数据 */
    doCreateNodeData: ICreateNodeData;
    /** 清除新增节点标记 */
    clearCreateNodeData: () => any;
}

class WorkflowSvgPanel extends React.Component<IProps, any> {

    public refs: {
        scrollPanel: any;
    };

    private nodeIs: number = -1;
    private lineIs: number = -1;

    constructor(props: any) {
        super(props);
        const nodes = new Array<NodeModel>();
        const lines = new Array<LineModel>();

        node.map((item) => {
            const nodeModel: NodeModel = new NodeModel();
            nodeModel.parse(item, this.getScrollPosition());
            nodes.push(nodeModel);
        });
        line.map((item) => {
            const lineModel: LineModel = new LineModel();
            lineModel.parse(item, this.getScrollPosition());
            const from: NodeModel = _.find(nodes, (it) => it.id === item.fromId);
            const to: NodeModel = _.find(nodes, (it) => it.id === item.toId);
            lineModel.from = from;
            lineModel.to = to;
            lineModel.autoId();
            from.fromLines.push(lineModel);
            to.toLines.push(lineModel);
            lines.push(lineModel);
        });

        this.state = {
            operaType: this.props.operaType,
            node: nodes,
            line: lines,
        };
    }

    componentWillReceiveProps(nextProps: IProps, nextContext: any) {
        const {doCreateNodeData, operaType} = nextProps;
        if (doCreateNodeData) {
            const nodeModel: NodeModel = new NodeModel();
            nodeModel.parse(doCreateNodeData, this.getScrollPosition());
            nodeModel.id = this.getNextId();
            const {node} = this.state;
            // 检测碰撞
            const range: Range = nodeModel.range;
            const suggestRange: Range = getNodeSuggestRange(node, nodeModel.id, range);
            if (suggestRange) {
                nodeModel.resetPosition(suggestRange.x, suggestRange.y);
            }
            node.push(nodeModel);

            this.props.clearCreateNodeData();
        }

        this.setState({
            operaType
        });
    }

    getScrollPosition = (): Point => {
        const scrollPanel = ReactDOM.findDOMNode(this.refs.scrollPanel);
        if (scrollPanel) {
            return new Point(scrollPanel.scrollLeft, scrollPanel.scrollTop);
        } else {
            return new Point();
        }
    }

    onNodeChange = (e, id, param) => {
        const {key, value} = param;
        const nodeData: NodeModel = this.getNodeById(id);
        const { node } = this.state;
        switch (key) {
            case 'selectNode':
                this.setState({
                    selectId: value
                }); break;
            case 'dragMove':
                const {x, y} = value;
                nodeData.resetPosition(x, y);
                this.updateLineWhenDrag(id, nodeData);
                break;
            case 'dragEnd':
                // 拖动结束，碰撞检测
                const range: Range = value;
                const suggestRange: Range = getNodeSuggestRange(node, id, range);
                if (suggestRange && nodeData) {
                    nodeData.resetPosition(suggestRange.x, suggestRange.y);
                }
                break;
            default: ;
        }
    }

    updateLineWhenDrag = (id: string, nodeData: NodeModel) => {
        const dragNode = this.getNodeById(id);
        dragNode.fromLines.map((item) => this.drawLineWhenDrag(item.id, nodeData, true));
        dragNode.toLines.map((item) => this.drawLineWhenDrag(item.id, nodeData, false));
    }

    drawLineWhenDrag = (id: string, nodeData: NodeModel, from: boolean) => {
        const line: Line = this.refs[id] as Line;
        if (from) {
            line.updateFrom(nodeData);
        } else {
            line.updateTo(nodeData);
        }
    }


    onLinkedEnd = (startId: string, point: Point): void => {
        const { node, line } = this.state;
        const startNode = this.getNodeById(startId);
        const scrollPosition = this.getScrollPosition();
        point = point.add(scrollPosition);
        const endNode: NodeModel = getRangeByPoint(node, startId, point);
        if (endNode) {
            const lineModel: LineModel = new LineModel();
            lineModel.type = LineType.Bezier;
            lineModel.from = startNode;
            lineModel.to = endNode;
            lineModel.autoId();
            startNode.fromLines.push(lineModel);
            endNode.toLines.push(lineModel);
            if (!_.find(line, (item: LineModel) => item.id === lineModel.id)) {
                line.push(lineModel);
                this.setState({
                    line
                });
            } else {
                console.log(`Line${lineModel.id} has exist`);
            }
        }
    }

    getNodeById = (id): NodeModel => {
        const { node } = this.state;
        let result: NodeModel = _.find(node, (item: NodeModel) => item.id === id);
        return result;
    }

    render() {
        const { node, line, selectId, operaType } = this.state;
        const range: Range = getValidNodeRange();
        const className = ['workflow__tbody', 'workflow__content__bg', 'noselect'];
        if (OperaType.Linked === operaType) {
            className.push('workflow__linked');
        }
        const nodeProps = {
            operaType: operaType,
            selectId: selectId,
            onChange: this.onNodeChange,
            onLinkedEnd: this.onLinkedEnd,
        };
        const lineProps = {

        };
        return (
            <div ref='scrollPanel' className={classnames(className)}>
                <Paper width={range.width} height={range.height}  >
                    {
                        line.map((item: LineModel) => (<Line ref={item.id} key={item.id} {...item.getProps() } {...lineProps} />))
                    }
                    {
                        node.map((item: NodeModel) => (<NodeAdapter key={item.id} {...item.getProps() } {...nodeProps} />))
                    }
                </Paper>
            </div>
        );
    }

    getNextId = () => {
        this.nodeIs++;
        return `node-${this.nodeIs}`;
    }
    getNextLineId = () => {
        this.lineIs++;
        return `line-${this.lineIs}`;
    }
};

const mapStateToProps = (state: any) => {
    const { doCreateNodeData, operaType } = state.action;
    return { doCreateNodeData, operaType };
};


const mapDispatchToProps = (dispatch: any) => ({
    clearCreateNodeData: () => dispatch(clearCreateNodeData()),
});

export default connect(mapStateToProps, mapDispatchToProps)(WorkflowSvgPanel);
